#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/leds.h> /* 包含 struct led_classdev 结构体，及API*/
#include <linux/gpio/consumer.h> /* 包含新版本GPIO子系统： -gpiod- API*/


/*
* 私有结构体,  可自行增加所需成员, 但必须包含 struct led_classdev
*/
struct led_dev
{
	struct led_classdev cdev;
    struct gpio_desc *desc;
};

static char *name_red = "red";
static char *name_green = "green";
static char *name_yellow = "yellow";
static struct led_dev *led_device[3];




/* 
* （1）对 /sys/class/leds/red/brightness 等LED属性进行控制时，会回调此函数 
*/
static void led_control(struct led_classdev *led_cdev, enum led_brightness brightness)
{
	struct led_dev *led = container_of(led_cdev, struct led_dev, cdev);
    
	pr_info(" ENTER led_control  led(0x%p)->desc=0x%p \n", led, led->desc);	

    //根据设定，设置亮或灭
	if (brightness != LED_OFF) { 
		gpiod_set_value(led->desc , 1);
		pr_info(" gpiod_set_value(led->desc , 1) \n");		
	
	}else{
			
		gpiod_set_value(led->desc , 0);
		pr_info(" gpiod_set_value(led->desc , 0) \n");		
	}

    pr_info("EXIT led_control \n");
}

/*
* （1）在检测到DT中.compatible = "szhou,aml_led_class"的节点后，会自动调用此函数，实现初始化
*/
static int __init ledclass_plat_probe(struct platform_device *pdev)
{

	struct device *dev = &pdev->dev;
	int i, count, ret;

	pr_info( "ledclass_plat_probe enter\n");

	/* 获取amlled-gpios里面gpio的数量 */
	count = gpiod_count(dev, "amlled");
	if (count ==  -ENOENT)
		return -ENOENT;

	dev_info(dev, "gpiod_count = %d \n", count);

	/* 填充每个gpio-led对象，并注册到LED子系统 */
	for(i=0; i<count; i++){

		//struct led_dev *led_device;
        //struct gpio_desc *desc = NULL;
	
		led_device[i] = devm_kzalloc(dev, sizeof(struct led_dev), GFP_KERNEL);
		if (!led_device[i])
			return -ENOMEM;
        
		/* 申请并初始化gpio描述符，电平初始化为GPIOD_OUT_LOW，即设为输出脚、低电平 */
		led_device[i]->desc = gpiod_get_index(dev, "amlled", i, GPIOD_OUT_LOW);
		pr_info(" ledclass_plat_probe  led(0x%p)->desc=0x%p \n", led_device[i], led_device[i]->desc);	
		
		switch(i){

			case 0:
				led_device[0]->cdev.name = name_red;
				pr_info("zs, 000000000   \n");
				//led_device->cdev.default_trigger = "heartbeat";//红灯将使用默认的闪烁程序，若启用，则要添加类似互斥锁
			    break;
			case 1:
				led_device[1]->cdev.name = name_green;
				pr_info("zs, 111111111111111   \n");				
			    break;
			case 2:
				led_device[2]->cdev.name = name_yellow;
				pr_info("zs, 22222222222   \n");				
			    break;	
			default:
				pr_info( "zs, i=%d, default \n", i);
				
		}

        pr_info("zs, 33333333333333   \n");	

		/* 初始化亮度值，以及LED的控制函数 */
		led_device[i]->cdev.brightness = LED_OFF;
		led_device[i]->cdev.brightness_set = led_control;
		
		/* 注册到LED子系统 */
		ret = devm_led_classdev_register(dev, &led_device[i]->cdev);
		pr_info( "zs, platform_probe  devm_led_classdev_register [%d], cdev->name=%s \n", i,  led_device[i]->cdev.name);
		if (ret) {
			dev_err(dev, "failed to register the led %s\n", led_device[i]->cdev.name);
			return ret;
		}
		
	}

	dev_info(dev, "ledclass_plat_probe exit\n");
	return 0;
}

static int __exit ledclass_plat_remove(struct platform_device *pdev)
{
    int i=0;
	dev_info(&pdev->dev, "ledclass_plat_remove enter\n");

    for(i=0; i<3; i++)
    { 
        gpiod_put(led_device[i]->desc);  //释放GPIO描述符      
    }

	dev_info(&pdev->dev, "ledclass_plat_remove exit\n");
	return 0;
}


/*
* 设备树的匹配属性 .compatible ，需完全相同才会匹配
*/
static const struct of_device_id my_of_ids[] = {
	{ .compatible = "szhou,aml_led_class"},
	{},
};

MODULE_DEVICE_TABLE(of, my_of_ids);


/*
* led_platform_driver 结构体
*/
static struct platform_driver led_platform_driver = {
	.probe = ledclass_plat_probe,
	.remove = ledclass_plat_remove,
	.driver = {
		.name = "aml_class_leds",
		.of_match_table = my_of_ids,
		.owner = THIS_MODULE,
	}
};


/*
* 注册 led_platform_driver 结构体到Platform子系统
*/
static int aml_GpioLedClass_plat_init(void)
{
	int ret_val;
	pr_info("aml_GpioLedClass_plat_init enter\n");

	ret_val = platform_driver_register(&led_platform_driver);
	if (ret_val !=0){
		pr_err("platform value returned %d\n", ret_val);
		return ret_val;
	}

	pr_info("aml_GpioLedClass_plat_init exit\n");
	return 0;
}

/*
* 从Platform子系统注销 led_platform_driver 结构体
*/
static void aml_GpioLedClass_plat_exit(void)
{
	pr_info("aml_GpioLedClass_plat_exit enter\n");

	platform_driver_unregister(&led_platform_driver);

	pr_info("aml_GpioLedClass_plat_exit exit\n");
}

module_init(aml_GpioLedClass_plat_init);
module_exit(aml_GpioLedClass_plat_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("szhou <66176468@qq.com>");
MODULE_DESCRIPTION("simple say[5]: led-class device driver");

